#include "FastUInt.h"
#include "GreatUInt.h"
#include <iostream>
#include <sstream>
#include <iomanip>

//accessors

UInt FastUInt::GetNumDigits() const
{
	return GreatUInt(*this).GetNumDigits();
}

UInt FastUInt::GetDigit(const UInt i_digitNum) const
{
	return GreatUInt(*this).GetDigit(i_digitNum);
}

UInt FastUInt::DigitSum() const
{
	return GreatUInt(*this).DigitSum();
}

void FastUInt::ToString( std::string& o_string ) const
{
	o_string.clear();
	std::ostringstream sstream;
	sstream << *this;
	o_string = sstream.str();
}

//void FastUInt::GetAs( FastInt& o_fastInt ) const
//{
//	o_fastInt.Assign(*this);
//}

//modifiers

//not inlined due to dependency to GreatUInt interface
FastUInt& FastUInt::Assign(const std::string& i_string)
{
	GreatUInt greatUInt(i_string);
	return Assign(greatUInt);
}

FastUInt& FastUInt::Assign( const GreatUInt& i_greatUInt )
{
	const size_t greatUIntSize = i_greatUInt.m_data.size();
	m_data.clear();
	m_data.reserve(greatUIntSize);
	FastUInt factor(1, greatUIntSize);
	FastUInt temp(0, greatUIntSize);
	for(size_t i=0; i<greatUIntSize; ++i)
	{
		if(i)
			factor*= GreatUInt::MAX_ELEMENT_VALUE;
		temp= factor;
		temp*= i_greatUInt.m_data[i];
		operator+=(temp);
	}
	
	return *this;
}

FastUInt& FastUInt::operator++()
{
	size_t i=0;
	while(i<m_data.size() && m_data[i]==MAX_UINT)
	{
		m_data[i] = 0;
		++i;
	}

	if(i<m_data.size())
		++m_data[i];
	else
		m_data.push_back(1);
	
	return *this;
}

FastUInt& FastUInt::operator--()
{
	//behave like UInt
	if(IsZero())
	{
		m_data[0] = MAX_UINT;
		return *this;
	}

	size_t i=0;
	while(m_data[i]==0)
	{
		m_data[i] = MAX_UINT;
		++i;
	}

	--m_data[i];
	if(m_data[i] == 0 && (i==m_data.size()-1))
		m_data.pop_back();

	return *this;
}

FastUInt& FastUInt::operator+=(const UInt i_num)
{
	const bool notOverFlow = (i_num <= MAX_UINT - m_data[0]);
	m_data[0] += i_num;
	if(notOverFlow)
		return *this;

	size_t i=1;
	while(i<m_data.size() && m_data[i]==MAX_UINT)
	{
		m_data[i] = 0;
		++i;
	}

	if(i<m_data.size())
		++m_data[i];
	else
		m_data.push_back(1);

	return *this;
}

FastUInt& FastUInt::operator+=(const FastUInt& i_that)
{
	UInt32 reminder = 0;
	if(m_data.size() < i_that.m_data.size())
	{
		m_data.resize(i_that.m_data.size(), 0);

		for(size_t i=0; i<m_data.size(); ++i)
		{
			UInt64 temp = i_that.m_data[i];
			temp += m_data[i];
			temp += reminder;
			m_data[i] = static_cast<UInt>(temp);
			reminder = temp >> 32;
		}

		if(reminder>0)
			m_data.push_back(reminder);
	}
	else
	{
		size_t i;
		for(i=0; i<i_that.m_data.size(); ++i)
		{
			UInt64 temp = i_that.m_data[i];
			temp += m_data[i];
			temp += reminder;
			m_data[i] = static_cast<UInt>(temp);
			reminder = temp >> 32;
		}

		while(reminder!=0)
		{
			if(i==m_data.size())
			{
				m_data.push_back(reminder);
				reminder = 0;
			}else{
				UInt64 temp = reminder;
				temp += m_data[i];
				m_data[i] = static_cast<UInt>(temp);
				reminder = temp >> 32;
				++i;
			}
		}
	}

	return *this;	
}

FastUInt& FastUInt::operator-=(const UInt i_num)
{
	if(m_data[0] >= i_num)
	{
		m_data[0]-= i_num;
	}else{
		m_data[0]-= i_num; //relying on overflow
		size_t i= 1;
		if(m_data.size() > 1)
		{
			bool found = false;
			while(!found)
			{
				if(m_data[i]-- != 0) //wanted side effect, relying on overflow
					found = true;
				else
					++i;
			}
		}
		if(m_data[i]==0 && i==m_data.size()-1)
			m_data.pop_back();
	}

	return *this;
}

FastUInt& FastUInt::operator-=(const FastUInt& i_that)
{
	if(*this<i_that)
		return SetZero()-=1;

	size_t i;
	bool report=false;
	for(i= 0; i< i_that.m_data.size(); ++i)
	{
		if(report)
		{
			if(m_data[i]-- == 0) //wanted side effect, relying on overflow
				report = true;
			else
				report = m_data[i]<i_that.m_data[i];
		}else{
			report = m_data[i]<i_that.m_data[i];
		}
		m_data[i]-=i_that.m_data[i];
	}

	while(report)
	{
		if(m_data[i]-- != 0) //wanted side effect, relying on overflow
			report= false;
		else
			++i;
	}


	//TODO
	return *this;
}

FastUInt& FastUInt::operator*=(const FastUInt& i_that)
{
	//TODO
	return *this;
}

#pragma warning( push )
#pragma warning( disable : 4244 ) //possible loss of data from UInt64 and UInt32... feature!
FastUInt& FastUInt::operator*=(const UInt i_factor)
{
	UInt32 reminder = 0;
	for(size_t i=0; i<m_data.size(); ++i)
	{
		UInt64 temp = i_factor;
		temp*=m_data[i];
		temp += reminder;
		m_data[i] = temp;
		reminder = temp >> 32;
	}
	
	if(reminder>0)
		m_data.push_back(reminder);
	
	return *this;
}
#pragma warning( pop )

FastUInt& FastUInt::operator&=(const FastUInt& i_that)
{
	if(m_data.size() < i_that.m_data.size())
		m_data.resize(i_that.m_data.size());

	for(size_t i=0; i<i_that.m_data.size(); ++i)
		m_data[i] &= i_that.m_data[i];

	return *this;
}

FastUInt& FastUInt::operator|=(const FastUInt& i_that)
{
	if(m_data.size() < i_that.m_data.size())
		m_data.resize(i_that.m_data.size());

	for(size_t i=0; i<i_that.m_data.size(); ++i)
		m_data[i] |= i_that.m_data[i];

	return *this;
}

FastUInt& FastUInt::operator^=(const FastUInt& i_that)
{
	if(m_data.size() < i_that.m_data.size())
		m_data.resize(i_that.m_data.size());

	for(size_t i=0; i<i_that.m_data.size(); ++i)
		m_data[i] ^= i_that.m_data[i];

	return *this;
}

FastUInt& FastUInt::operator<<=(const UInt i_factor)
{
	//TODO
	return *this;
}

FastUInt& FastUInt::operator>>=(const UInt i_factor)
{
	//m_data.erase(m_data.begin(), m_data.begin() + i_factor/32);

	//TODO
	return *this;
}

FastUInt& FastUInt::DivWithRem(const FastUInt& i_toBeDivided, const FastUInt& i_divisor, bool i_storeQuotient, FastUInt* o_other)
{
	//TODO
	return *this;
}

//FastUInt& FastUInt::DivWithRemainder(const FastUInt& i_divisor, FastUInt& o_remainder)
//{
//	if(this==&o_remainder)
//		return SetInvalid();
//	
//	if(i_divisor.IsZero())
//	{
//		o_remainder.SetInvalid();
//		return SetInvalid();
//	}
//
//	if(this==&i_divisor)
//	{
//		o_remainder.SetZero();
//		return SetOne();
//	}
//
//	if(IsLessThan(i_divisor))
//	{
//		Swap(o_remainder);
//		return SetZero();
//	}
//
//	// o_remainder = 
//	o_remainder.Mul(operator/=(i_divisor),i_divisor).Sub(*this,o_remainder);
//	//TODO
//	return *this;
//}

FastUInt& FastUInt::ApplyExp(const UInt i_exp)
{
	if(i_exp==0)
		return SetOne();

	//TODO: improve efficiency
	FastUInt temp(*this);
	for(UInt i=1; i<i_exp; ++i)
	{
		operator*=(temp);
	}
	
	return *this;
}

// non members

std::ostream& operator<<(std::ostream& os, const FastUInt& i_data)
{
	os << GreatUInt(i_data);
	return os;
}


Int Compare( const FastUInt& i_left, const FastUInt& i_right )
{
	const size_t leftDataSize = i_left.m_data.size(); 
	const size_t rightDataSize = i_right.m_data.size(); 
	if(leftDataSize < rightDataSize)
		return 1;
	if(leftDataSize > rightDataSize)
		return -1;

	size_t i=leftDataSize-1;
	while((i_left.m_data[i] == i_right.m_data[i]) && (i!=0))
		--i;

	if(i_left.m_data[i] == i_right.m_data[i])
		return 0;
	return i_left.m_data[i] < i_right.m_data[i] ? 1 : -1;
	
}